/*
 * F_Caches.c
 *
 *  Created on: 16/11/2011
 *      Author: utn_so
 */

#include "../../headers/GlobalHeaderPFS.h"
#include "../../headers/Constantes_PFS.h"

extern tNodoCache * lCaches;
extern pthread_mutex_t mutex_caches;
extern int32_t cantBloquesXCache;
extern struct_config configuracion;
extern volatile sig_atomic_t dump;
extern t_log *logger;
extern int32_t cantEntradasDir;
extern tnodoCacheEntradasDir * cacheEntradasDir;
extern int32_t cantEntradasDirMax;
extern pthread_mutex_t mutex_cache_entradasDir;

/*-------------------------------------------------------------------------*/
/**

 */
void crearCache(const char * path){

	tNodoCache * nodoAAgregar;
	nodoAAgregar = (tNodoCache *) malloc(sizeof(tNodoCache));
	//memset(&nodoAAgregar,0,sizeof(tNodoCache));
	pthread_mutex_init (&nodoAAgregar->enUso, NULL);
	nodoAAgregar->path = path;
	nodoAAgregar->cant=0;
	nodoAAgregar->cache=NULL;
	pthread_mutex_lock (&mutex_caches);
		if (lCaches==NULL){
			lCaches = nodoAAgregar;
			nodoAAgregar->sgte=NULL;
		}else{
		nodoAAgregar->sgte= lCaches;
		lCaches = nodoAAgregar;
		}
	pthread_mutex_unlock (&mutex_caches);
}

/*-------------------------------------------------------------------------*/
/**

 */
tNodoCache * conseguirCache(const char * path){

	tNodoCache * p;
	p = lCaches;
	tNodoCache * nodoBuscado=NULL;
//	char path1[20];
//	memset(path1,' ',20);
//	strcpy(path1,path);
//	char path2[20];
//	memset(path2,' ',20);
//	strcpy(path2,p->path);
	while(p!=NULL){
		if(!strcmp(p->path,path)){
			nodoBuscado = p;
			return nodoBuscado;
		}
		p = p->sgte;
	}
	return nodoBuscado;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t agregarBloqueCache(tNodoCache * nodoCache,uint32_t nroBloque,tBloque contenidoBloque,uint8_t modificado){

	tCache * bloqueAAgregar=NULL;
	bloqueAAgregar = (tCache *) malloc(sizeof(tCache));
	bloqueAAgregar->nroBloque=nroBloque;
	bloqueAAgregar->modificado=modificado;
	bloqueAAgregar->orden=modificado*2+1;
	memcpy(&(bloqueAAgregar->data),&(contenidoBloque.bufferBloque),TAMANO_BLOQUE);
	bloqueAAgregar->sgte=nodoCache->cache;
	nodoCache->cache=bloqueAAgregar;
	nodoCache->cant++;
	return 1;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t borrarBloqueCache(tNodoCache * nodoCache){
	uint32_t ordenAReemplazar = nodoCache->cache->orden;
	tCache * bloqueAReemplazar=nodoCache->cache;
	tCache * bloqueAnt=NULL;
	tCache * temp= nodoCache->cache;
	while(temp->sgte!=NULL){
		if((temp->sgte->orden)<ordenAReemplazar){
			ordenAReemplazar = temp->sgte->orden;
			bloqueAnt = temp;
			bloqueAReemplazar = temp->sgte;
		}
		temp=temp->sgte;
	}
	if(bloqueAReemplazar == nodoCache->cache){
		nodoCache->cache = nodoCache->cache->sgte;
	}else{
		if(bloqueAnt!=NULL){
			bloqueAnt->sgte=bloqueAReemplazar->sgte;
		}
	}

	if(bloqueAReemplazar->modificado==1)
	//pedirEscrituraDeCluster(bloqueAReemplazar->nroCluster,&(bloqueAReemplazar->data));
	pedirEscrituraDeBloque(bloqueAReemplazar->nroBloque,&(bloqueAReemplazar->data));
	free(bloqueAReemplazar);
	nodoCache->cant--;
	return 1;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t leerBloqueCache(tNodoCache * nodoCache,uint32_t nroBloque,tBloque * contenidoBloque){
	tCache * bloque=nodoCache->cache;
	pthread_mutex_lock (&(nodoCache->enUso));
	while(bloque!=NULL){
	if(nroBloque==bloque->nroBloque){
		memcpy(&(contenidoBloque->bufferBloque),&(bloque->data),TAMANO_BLOQUE);
		bloque->orden++;
		pthread_mutex_unlock (&nodoCache->enUso);
		return 1;
		}
	bloque = bloque->sgte;
	}
	pthread_mutex_unlock (&(nodoCache->enUso));
	return 0;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t escribirBloqueCache(tNodoCache * nodoCache,uint32_t nroBloque,tBloque * contenidoBloque){
	tCache * bloque=nodoCache->cache;
	pthread_mutex_lock (&(nodoCache->enUso));
	while(bloque!=NULL){
	if(nroBloque==bloque->nroBloque){
		memcpy(&(bloque->data),&(contenidoBloque->bufferBloque),TAMANO_BLOQUE);
		bloque->orden++;
		bloque->orden++;
		bloque->modificado=1;
		pthread_mutex_unlock (&nodoCache->enUso);
		return 1;
		}
	bloque = bloque->sgte;
	}
	pthread_mutex_unlock (&(nodoCache->enUso));
	return 0;
}


/*-------------------------------------------------------------------------*/
/**

 */
void crearArchCache(){
		//if (dump ==1){
		//	perror("Señal que ya esta en ejecucion");
		//}else{
		//	dump=1;
			FILE * file = fopen(configuracion.PathArchivoCache,"ab");
			uint32_t var;
			tNodoCache * p=lCaches;
			tCache * bloque;
			pthread_mutex_lock(&mutex_caches);
			while(p!=NULL){
				fprintf(file,"Archivo: %s\n",p->path);
				fprintf(file,"Tamaño de Bloque de Cache: %d\n",TAMANO_BLOQUE);
				fprintf(file,"Cantidad de Bloques de Cache: %d\n",TAMANO_CACHE/TAMANO_BLOQUE);
				bloque = lCaches->cache;
				for (var=0;var<p->cant;var++){
					fprintf(file,"Contenido de Bloque de Cache %d:\n",var+1);
					fprintf(file,"%s\n",bloque->data.bufferBloque);
					bloque=bloque->sgte;
				}
			fprintf(file,"-----------------------------------------\n");

			}
		//	dump=0;
			pthread_mutex_unlock(&mutex_caches);
			fclose(file);
			return;
		}
		//return dump;
	//}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t pedirLecturaClusterArchs(const char * path,uint32_t nroBloque, tBloque * contenidoBloque){

	if(configuracion.tamanoCache==0){
		//pedirLecturaDeCluster(nroCluster,contenidoCluster);
		pedirLecturaDeBloque(nroBloque,contenidoBloque);
		return 1;
	}
	tNodoCache * cache = conseguirCache(path);

	if(cache==NULL){
		crearCache(path);
		cache = conseguirCache(path);
	}

	if(leerBloqueCache(cache,nroBloque,contenidoBloque)){
		if(configuracion.logsActivados)
		log_info(logger, "PFS", "Lei el sector nro : %i de la cache de: %s\n",nroBloque,path);
		return 1;
	}else{
		//pedirLecturaDeCluster(nroCluster,contenidoCluster);
		pedirLecturaDeBloque(nroBloque,contenidoBloque);
		if(cache->cant<cantBloquesXCache){
		pthread_mutex_lock(&(cache->enUso));
		agregarBloqueCache(cache,nroBloque,*contenidoBloque,0);
		if(configuracion.logsActivados)
		log_info(logger, "PFS", "Agregue el sector nro : %i a la cache de: %s\n",nroBloque,path);
		pthread_mutex_unlock(&(cache->enUso));
		}else{
		pthread_mutex_lock(&(cache->enUso));
		borrarBloqueCache(cache);
		if(configuracion.logsActivados)
		log_info(logger, "PFS", "Elimine el sector nro : %i a la cache de: %s\n",nroBloque,path);
		agregarBloqueCache(cache,nroBloque,*contenidoBloque,0);
		if(configuracion.logsActivados)
		log_info(logger, "PFS", "Agregue el sector nro : %i a la cache de: %s\n",nroBloque,path);
		pthread_mutex_unlock(&(cache->enUso));
		}
	}

	return 1;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t pedirEscrituraClusterArchs(const char * path,uint32_t nroBloque, tBloque * contenidoBloque){

	if(configuracion.tamanoCache==0){
		//pedirEscrituraDeCluster(nroCluster,contenidoCluster);
		pedirEscrituraDeBloque(nroBloque,contenidoBloque);
		return 1;
	}
	tNodoCache * cache = conseguirCache(path);
	if(escribirBloqueCache(cache,nroBloque,contenidoBloque)){
		if(configuracion.logsActivados)
		log_info(logger, "PFS", "Escribi el sector nro : %i en la cache de: %s\n",nroBloque,path);
		return 1;
	}else{
		if(cache->cant<cantBloquesXCache){
		pthread_mutex_lock(&(cache->enUso));
		agregarBloqueCache(cache,nroBloque,*contenidoBloque,1);
		if(configuracion.logsActivados)
		log_info(logger, "PFS", "Agregue el sector nro : %i a la cache de: %s\n",nroBloque,path);
		pthread_mutex_unlock(&(cache->enUso));
		}else{
		pthread_mutex_lock(&(cache->enUso));
		borrarBloqueCache(cache);
		if(configuracion.logsActivados)
		log_info(logger, "PFS", "Elimine el sector nro : %i a la cache de: %s\n",nroBloque,path);
		agregarBloqueCache(cache,nroBloque,*contenidoBloque,1);
		if(configuracion.logsActivados)
		log_info(logger, "PFS", "Agregue el sector nro : %i a la cache de: %s\n",nroBloque,path);
		pthread_mutex_unlock(&(cache->enUso));
		}
	}

	return 1;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t agregarEntradasDirCache(const char * path, tDirectoryEntries * entradas){

	//tnodoCacheEntradasDir * nodoAAgregar=NULL;
	tnodoCacheEntradasDir * nodoAAgregar = (tnodoCacheEntradasDir *) malloc(sizeof(tnodoCacheEntradasDir));
	memcpy(&(nodoAAgregar->entradas),&(*entradas),sizeof(tDirectoryEntries));
	//memset((nodoAAgregar->path),' ',20);
	//strcpy(nodoAAgregar->path,path);
	nodoAAgregar->path = (char *) malloc (strlen(path)+1);
	strcpy(nodoAAgregar->path,path);
	//memcpy(&(nodoAAgregar->path),&(path),strlen(path)+1);
	//nodoAAgregar->path=path;
	nodoAAgregar->orden=1;
	nodoAAgregar->sgte= cacheEntradasDir;
	if(cantEntradasDirMax>cantEntradasDir){
		pthread_mutex_lock (&(mutex_cache_entradasDir));
		cacheEntradasDir=nodoAAgregar;
		if(nodoAAgregar->path==NULL){
			printf("pUTO");
		}
		pthread_mutex_unlock (&(mutex_cache_entradasDir));
	}else{
	pthread_mutex_lock (&(mutex_cache_entradasDir));
	borrarUnaEntradaDirCache();
	cacheEntradasDir=nodoAAgregar;
	pthread_mutex_unlock (&(mutex_cache_entradasDir));
	}
	cantEntradasDir++;

	return 1;
}


/*-------------------------------------------------------------------------*/
/**

 */
uint32_t leerEntradaDirCache(const char * path, tDirectoryEntries * entradas){

	tnodoCacheEntradasDir * p= cacheEntradasDir;

	pthread_mutex_lock (&(mutex_cache_entradasDir));

	while(p!=NULL){
		if(!strcmp(p->path,path)){
			memcpy(&(*entradas),&(p->entradas),sizeof(tDirectoryEntries));
			p->orden++;
			pthread_mutex_unlock (&(mutex_cache_entradasDir));
			if(configuracion.logsActivados)log_info(logger, "PFS", "Cache--Dir: Lei entrada de %s\n",path);
			return 1;
		}
		p = p->sgte;
	}
	pthread_mutex_unlock (&(mutex_cache_entradasDir));
	return 0;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t borrarUnaEntradaDirCache(){

	tnodoCacheEntradasDir * remover =  cacheEntradasDir;
	tnodoCacheEntradasDir * p = cacheEntradasDir;
	tnodoCacheEntradasDir * Antp = NULL;

	if(cantEntradasDir==0)return 0;
	if(cantEntradasDir==1){
		cacheEntradasDir=NULL;
		free(p);
		return 1;
	}
	int32_t orden=p->orden;
	while((p->sgte)!=NULL){
		if(orden>=(p->sgte->orden)){
			orden=p->sgte->orden;
			Antp=p;
			remover=p->sgte;
		}
		p = p->sgte;
	}
	if(Antp==NULL){
		cacheEntradasDir=cacheEntradasDir->sgte;
	}else{
		Antp->sgte=remover->sgte;
	}
	free((remover->path));
	free(remover);

	cantEntradasDir--;

	return 1;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t borrarEntradaDirCache(const char * path){

	tnodoCacheEntradasDir * p= cacheEntradasDir;
	tnodoCacheEntradasDir * Antp=NULL;
	pthread_mutex_lock (&(mutex_cache_entradasDir));
	if((!strcmp(p->path,path))){
		cacheEntradasDir=cacheEntradasDir->sgte;
		free((p->path));
		free(p);
		pthread_mutex_unlock (&(mutex_cache_entradasDir));
		cantEntradasDir--;
		return 1;
	}
	while(p->sgte!=NULL){
		if(!strcmp(p->sgte->path,path)){
			Antp=p;
			p=p->sgte;
			Antp->sgte=p->sgte;
			free((p->path));
			free(p);
			cantEntradasDir--;
			pthread_mutex_unlock (&(mutex_cache_entradasDir));
			return 1;
		}
		p = p->sgte;
	}
	pthread_mutex_unlock (&(mutex_cache_entradasDir));
	return 0;
}

///*-------------------------------------------------------------------------*/
///**
//
// */
//uint32_t pedirLecturaClusterArchs(const char * path,uint32_t nroCluster, tCluster * contenidoCluster){
//
//	if(configuracion.tamanoCache==0){
//		pedirLecturaDeCluster(nroCluster,contenidoCluster);
//		return 1;
//	}
//	tNodoCache * cache = conseguirCache(path);
//
//	if(cache==NULL){
//		crearCache(path);
//		cache = conseguirCache(path);
//	}
//
//	if(leerBloqueCache(cache,nroCluster,contenidoCluster)){
//		return 1;
//	}else{
//		pedirLecturaDeCluster(nroCluster,contenidoCluster);
//		if(cache->cant<cantBloquesXCache){
//		pthread_mutex_lock(&(cache->enUso));
//		agregarBloqueCache(cache,nroCluster,*contenidoCluster,0);
//		pthread_mutex_unlock(&(cache->enUso));
//		}else{
//		pthread_mutex_lock(&(cache->enUso));
//		borrarBloqueCache(cache);
//		agregarBloqueCache(cache,nroCluster,*contenidoCluster,0);
//		pthread_mutex_unlock(&(cache->enUso));
//		}
//	}
//
//	return 1;
//}
//
///*-------------------------------------------------------------------------*/
///**
//
// */
//uint32_t pedirEscrituraClusterArchs(const char * path,uint32_t nroCluster, tCluster * contenidoCluster){
//
//	if(configuracion.tamanoCache==0){
//		pedirEscrituraDeCluster(nroCluster,contenidoCluster);
//		return 1;
//	}
//	tNodoCache * cache = conseguirCache(path);
//	if(escribirBloqueCache(cache,nroCluster,contenidoCluster)){
//		return 1;
//	}else{
//		if(cache->cant<cantBloquesXCache){
//		pthread_mutex_lock(&(cache->enUso));
//		agregarBloqueCache(cache,nroCluster,*contenidoCluster,1);
//		pthread_mutex_unlock(&(cache->enUso));
//		}else{
//		pthread_mutex_lock(&(cache->enUso));
//		borrarBloqueCache(cache);
//		agregarBloqueCache(cache,nroCluster,*contenidoCluster,1);
//		pthread_mutex_unlock(&(cache->enUso));
//		}
//	}
//
//	return 1;
//}
